Microsoft DirectX 8.1 (C++)

Closed Captioning

Closed-captioned pins come in several varieties:

The pin type determines the connection requirements:

Closed-Captioning Pin: For preview, connect this pin to the CC Decoder filter.

On Microsoft� Windows� 98, Windows 2000, and Windows Me, connect that filter the Line 21 Decoder, and connect that filter to Pin 1 on the Overlay Mixer. (Pin 0 is reserved for the primary video stream.) If you call IGraphBuilder::Connect, the filter graph manager automatically adds the Line 21 Decoder. For file capture, connect to a mux filter, not the Overlay Mixer.

On Windows XP Home Edition and Windows XP Professional: Connect the CC Decoder to the Line 21 Decoder 2, and connect that filter to the Video Mixing Renderer.

Hardware slicing closed-captioning pin: If the hardware device performs its own decoding (slicing) of the closed-captioning data, the capture filter will expose a pin with the category (PINNAME_VIDEO_CC_CAPTURE). The name given to this pin might vary depending on the hardware vendor. Connect this pin directly to the CC Decoder; no Tee is needed. The CC Decoder automatically detects when it is receiving pre-decoded data, and in this case it passes it through unchanged to the Line 21 Decoder.

VBI Pin: Connect this pin to the Tee/Sink-to-Sink Converter filter (also called the VBI Infinite Tee). This filter splits the VBI data into separate streams. Connect this filter to the CC Decoder filter. For preview, connect the CC Decoder to the Overlay Mixer or Video Mixing Renderer, as described previously. For file capture, connect it to a mux filter.

VBI Video Port Pin: On Microsoft� Windows� 98, Windows 2000, and Windows Me,  connect this pin to the VBI Surface Allocator filter (CLSID_VBISurfaces). This filter handles the Microsoft� DirectDraw� surfaces for the VBI data. If a filter has both a VBI pin and a VBI video port pin, connect both of them. On Windows XP Home Edition and Windows XP Professional, connect this pin to the Video Port Manager.

For preview, connect the capture filter's preview pin to Pin 0 on the Overlay Mixer (or to the Video Mixing Renderer as described above). This applies to PIN_CATEGORY_PREVIEW pins as well as PIN_CATEGORY_VIDEOPORT pins. The Overlay Mixer or VMR will overlay the closed captions onto the primary video.

The following diagram shows a preview graph with a VBI pin and a VBI video port pin.

Filter graph with closed captioning

The Tee/Sink-to-Sink Converter and the CC Decoder filters are actually WDM mini-drivers, wrapped by the KSProxy filter. Therefore, you cannot create these filters by calling CoCreateInstance. Instead, use the System Device Enumerator to enumerate each filter's device category:

Filter Category
Tee/Sink-to-Sink Converter AM_KSCATEGORY_SPLITTER (WDM streaming tee/splitter)
CC Decoder AM_KSCATEGORY_VBICODEC (WDM streaming VBI codecs)

Within the category, test each filter's friendly name until you find a matching string. Then call IMoniker::BindToObject to create an instance of the filter.

The following example code creates a Tee/Sink-to-Sink Converter filter. For brevity, it omits error checking.

IBaseFilter *pFilter = NULL; // Pointer to receive the filter instance.

// Create the system device enumerator.
ICreateDevEnum *pDevEnum;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
    IID_ICreateDevEnum, (void**)&pDevEnum);

// Create a class enumerator for the tee/splitter category.
IEnumMoniker *pEnum;
pDevEnum->CreateClassEnumerator(AM_KSCATEGORY_SPLITTER, &pEnum, 0);

// Enumerate devices within this category.
IMoniker *pMoniker;
ULONG cFetched;
while(pEnum->Next(1, &pMoniker, &cFetched) == S_OK)
{
    IPropertyBag *pBag;
    pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);

    // Check the friendly name.
    VARIANT var;
    var.vt = VT_BSTR;
    pBag->Read(L"FriendlyName", &var, NULL);
    if (lstrcmpiW(var.bstrVal, L"Tee/Sink-to-Sink Converter") == 0)
    {
        // This is the right filter.
        pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pFilter);
        SysFreeString(var.bstrVal);
        pBag->Release();
        pMoniker->Release();
        break;
    }
    SysFreeString(var.bstrVal);
    pBag->Release();
    pMoniker->Release();
}
pEnum->Release();
pDevEnum->Release();